home *** CD-ROM | disk | FTP | other *** search
/ NeXT Enterprise Objects Framework 1.1 / NeXT Enterprise Objects Framework 1.1.iso / NextDeveloper / Examples / EnterpriseObjects / MasteringDetails / EOFExtensions.subproj / KeyGenerator.m < prev    next >
Encoding:
Text File  |  1995-02-18  |  6.2 KB  |  167 lines

  1. /* KeyGenerator.m created by cfeder on Wed 26-Oct-1994 */
  2.  
  3. #import "KeyGenerator.h"
  4. #import "ValueForKey.h"
  5.  
  6. // Default implementation for all EOs to generate their own primary keys
  7. @implementation NSObject (assignPrimaryKey)
  8. - (void)assignPrimaryKeyForEntity:(EOEntity *)entity
  9. {
  10.     NSString *primaryKeyName = [[entity primaryKeyAttributeNames] objectAtIndex:0];
  11.     NSNumber *value = [NSNumber numberWithUnsignedInt:[KeyGenerator nexKeyForEntity:entity]];
  12.     NSDictionary *pkDict = [NSDictionary dictionaryWithObjects:&value forKeys:&primaryKeyName count:1];
  13.     [self takeValuesFromDictionary:pkDict];
  14. }
  15.  
  16. - (void)assignPrimaryKeyIfNotAlreadyPresentForEntity:(EOEntity *)entity
  17. {
  18.     // Figure out whether we already have a key assigned
  19.     BOOL hasKey = NO;
  20.     NSEnumerator *keys = [[entity primaryKeyAttributeNames] objectEnumerator];
  21.     NSString *key;
  22.     while (key = [keys nextObject]) {
  23.         id value = [self valueForKey:key];
  24.         if (value && (value != [EONull null]) && (![value isKindOfClass:[NSNumber class]] || ([value intValue]!=0))){
  25.             hasKey = YES;
  26.             break;
  27.         }
  28.     }
  29.  
  30.     // if we don't already have a primary key, then assign ourselves one
  31.     // get our current primary key values
  32.     if (!hasKey) {
  33.         [self assignPrimaryKeyForEntity:entity];
  34.     }
  35. }
  36. @end
  37.  
  38. // Assumptions:
  39. // Model contains:
  40. // - login information
  41. // - UniqueKey entity with attributes entity_name, and max_key
  42.  
  43.  
  44. @implementation KeyGenerator
  45. #define RESERVE_SET_SIZE 5
  46.  
  47.  
  48. - initWithEntity:(EOEntity *)anEntity dataSource:(EODatabaseDataSource *)aDataSource
  49. {
  50.     [super init];
  51.     entity = [anEntity retain];
  52.     dataSource = [aDataSource retain];
  53.     lastGranted = lastReserved = 0;
  54.     return self;
  55. }
  56.  
  57. - (unsigned)nextKey
  58. {
  59.     if (lastGranted >= lastReserved) {
  60.         // Go out to database and reserve a block of keys
  61.         EOQualifier    *tableNameQualifier;
  62.         EODatabaseChannel *channel = [dataSource databaseChannel];
  63.         EODatabaseContext *context = [channel databaseContext];
  64.         NSString *externalName = [entity externalName];
  65.         EOEntity *uniqueKeyEntity = [dataSource entity];
  66.  
  67.         if (!uniqueKeyEntity)
  68.             [NSException raise:NSInternalInconsistencyException format:@"%s: model does not contain UniqueKey entity '%@'", sel_getName(_cmd), [entity name]];
  69.  
  70.         if (![channel isOpen])
  71.             [channel openChannel];
  72.         [context beginTransaction];
  73.  
  74.         // This is the first time for this entity.  We need to get the current setting.
  75.         if (!currentMaxRecord) {
  76.             // Construct the qualifier for our name
  77.             tableNameQualifier = [[[EOQualifier alloc] initWithEntity:uniqueKeyEntity
  78.                                    qualifierFormat:@"%A= '%@'", @"entity_name", externalName] autorelease];
  79.             [channel selectObjectsDescribedByQualifier:tableNameQualifier fetchOrder:nil];
  80.             currentMaxRecord = [[channel fetchWithZone:[self zone]] retain];
  81.             [channel cancelFetch];
  82.         }
  83.  
  84.         if (currentMaxRecord) {
  85.             // attempt to update the max_key value
  86.             for(;;) {
  87.                 lastGranted = [[currentMaxRecord objectForKey:@"max_key"] unsignedIntValue];
  88.                 lastReserved = lastGranted + RESERVE_SET_SIZE;
  89.                 [currentMaxRecord setObject:[NSNumber numberWithUnsignedInt:lastReserved] forKey:@"max_key"];
  90.  
  91.                 if ([channel updateObject:currentMaxRecord] && [context commitTransaction])
  92.                     break;  // success!!
  93.  
  94.                 // Update failed.  Setup for another pass
  95.                 [context rollbackTransaction];
  96.                 [context beginTransaction];
  97.                 [channel refetchObject:currentMaxRecord];
  98.             }
  99.         } else {
  100.             // we have to insert a record for this entity
  101.             currentMaxRecord = [[EOGenericRecord alloc] initWithPrimaryKey:nil entity:uniqueKeyEntity];  // retained
  102.             [currentMaxRecord setObject:externalName forKey:@"entity_name"];
  103.             lastReserved = RESERVE_SET_SIZE;
  104.             lastGranted = 0;
  105.             [currentMaxRecord setObject:[NSNumber numberWithUnsignedInt:lastReserved] forKey:@"max_key"];
  106.             NSLog(@"Entity '%@' not in UniqueKey table.  Adding record: %@", [entity name], currentMaxRecord);
  107.             if (![channel insertObject:currentMaxRecord])
  108.                 [NSException raise:NSInternalInconsistencyException format:@"Failed to insert UniqueKey object for entity: %@", [entity name]];
  109.             [context commitTransaction];
  110.         }
  111.     }
  112.  
  113.     lastGranted++;        
  114.     return lastGranted;
  115. }
  116.  
  117. - (void)dealloc
  118. {
  119.     [entity release];
  120.     [dataSource release];
  121.     [super dealloc];
  122. }
  123.  
  124.  
  125. //
  126. // Class methods to automatically cache instances
  127. //
  128. static NSMutableDictionary *entityNameToGenerator;
  129.  
  130. + (EODatabaseDataSource *)dataSourceForModel:(EOModel *)model
  131. {
  132.     // use the datasource rendevous mechanism to do our caching for us
  133.     return [[EODatabaseDataSource alloc] initWithModelName:[model name]
  134.                                                 entityName:@"unique_key"
  135.                                               databaseName:nil
  136.                                                contextName:@"_UniqueKey"
  137.                                                channelName:nil];
  138. }
  139.  
  140. + keyGeneratorForEntity:(EOEntity *)anEntity
  141. {
  142.     KeyGenerator *kg = [entityNameToGenerator objectForKey:[anEntity name]];
  143.     if (!kg) {
  144.         EODatabaseDataSource *ds = [self dataSourceForModel:[anEntity model]];
  145.         if (!ds)
  146.             [NSException raise:NSInternalInconsistencyException format:@"%s: unable to allocation UniqueKey data source for entity '%@'", sel_getName(_cmd), [anEntity name]];
  147.         kg = [[KeyGenerator alloc] initWithEntity:anEntity dataSource:ds];
  148.  
  149.         if (kg) {
  150.             if (!entityNameToGenerator)
  151.                 entityNameToGenerator = [NSMutableDictionary new];
  152.             [entityNameToGenerator setObject:kg forKey:[anEntity name]];
  153.         }
  154.     }
  155.     return kg;
  156. }
  157.  
  158. + (unsigned)nexKeyForEntity:(EOEntity *)anEntity
  159. {
  160.     KeyGenerator *kg = [self keyGeneratorForEntity:anEntity];
  161.     if (!kg)
  162.         [NSException raise:NSInternalInconsistencyException format:@"%s: unable to create KeyGenerator for entity '%@'", sel_getName(_cmd), [anEntity name]];
  163.     return [kg nextKey]; 
  164. }
  165.  
  166. @end
  167.